package com.example.zz.example.mediarecorder;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Surface;
import android.view.View;
import android.view.WindowManager;
import android.widget.Toast;

import com.example.zz.example.R;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * targetsdk 28及以下才行
 * 主流程正常，部分场景有异常，看主要流程，录屏视频数据流转节点如下：
 * virtualDisplay -> surface -> mediaCodec -> mediaMuxer
 *
 * 而相机录像数据流程：
 * SurfaceView -> mediaCodec -> mediaMuxer
 *
 * 文件存储在根目录：storage/emulated/0/
 *
 */

public class ScreenRecordActivity extends AppCompatActivity {
    private static final int RECORDER_CODE = 0;
    private static final String TAG = "TAG";

    int width;
    int height;
    int dpi;

    MediaProjectionManager projectionManager;
    MediaProjection mediaProjection;
    MediaCodec mediaCodec;
    MediaMuxer mediaMuxer;

    Surface surface;
    VirtualDisplay virtualDisplay;
    private MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
    private int videoTrackIndex = -1;

    String filePath;
    private AtomicBoolean mQuit = new AtomicBoolean(false);
    private boolean muxerStarted = false;

    private ThreadPoolExecutor executor = new ThreadPoolExecutor(1,
            2,
            60,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(5));


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_screen_record);
        DisplayMetrics metric = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(metric);
        width = 720;
        height = 1280;
        dpi = 1;

        File file = new File(this.getExternalCacheDir(),
                "record-" + width + "x" + height + "-" + System.currentTimeMillis() + ".mp4");
        filePath = file.getAbsolutePath();

        projectionManager = (MediaProjectionManager) ScreenRecordActivity.this.getSystemService(MEDIA_PROJECTION_SERVICE);
    }

    public void startRecord(View view) {
        startActivityForResult(projectionManager.createScreenCaptureIntent(), RECORDER_CODE);
    }

    public void stopRecord(View view) {
        mQuit.set(true);
        Toast.makeText(this, "Recorder stop", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
//        通过projectionManager获取mediaProjection
        mediaProjection = projectionManager.getMediaProjection(resultCode, data);
        executor.execute(() -> {
            try {
                try {
//                    设置视频参数
                    MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
                    format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
                    format.setInteger(MediaFormat.KEY_BIT_RATE, 6000000);
                    format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
                    format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
//                    生成视频编码器
                    mediaCodec = MediaCodec.createEncoderByType("video/avc");
                    mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
//                    通过编码器创建surface，用来接受virtualDispay上的图像数据
                    surface = mediaCodec.createInputSurface();
                    mediaCodec.start();
//                    创建混合器
                    mediaMuxer = new MediaMuxer(filePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);

//                    创建虚拟显示，传入surface，由VirtualDisplay向Surface填充数据，由SurfaceFlinger管理，
//                    SurfaceFlinger将Layer读到到数据也通过GL叠加合成到VirtualDisplay上
                    virtualDisplay = mediaProjection.createVirtualDisplay(TAG + "-display",
                            width, height, dpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
                            surface, null, null);

                    while (!mQuit.get()) {
//                        从编码器中取出编码好的数据，没有dequeueInputBuffer取buffer，然后再把未编码数据填充进去，是因为api自动为我们做了这部分操作
                        int index = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000);
                        if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                            MediaFormat newFormat = mediaCodec.getOutputFormat();
                            videoTrackIndex = mediaMuxer.addTrack(newFormat);
                            mediaMuxer.start();
                            muxerStarted = true;
                        } else if (index >= 0) {
                            ByteBuffer encodedData = mediaCodec.getOutputBuffer(index);
                            if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
                                bufferInfo.size = 0;
                            }
                            if (bufferInfo.size == 0) {
                                encodedData = null;
                            }
                            if (encodedData != null) {
                                encodedData.position(bufferInfo.offset);
                                encodedData.limit(bufferInfo.offset + bufferInfo.size);
                                mediaMuxer.writeSampleData(videoTrackIndex, encodedData, bufferInfo);
                            }
                            mediaCodec.releaseOutputBuffer(index, false);
                        }
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            } finally {
                release();
            }
        });
        Toast.makeText(this, "Recorder is running...", Toast.LENGTH_SHORT).show();
//        moveTaskToBack(true);
    }

    private void release() {
        if (mediaCodec != null) {
            mediaCodec.stop();
            mediaCodec.release();
            mediaCodec = null;
        }
        if (virtualDisplay != null) {
            virtualDisplay.release();
        }
        if (mediaProjection != null) {
            mediaProjection.stop();
        }
        if (mediaMuxer != null) {
            mediaMuxer.release();
            mediaMuxer = null;
        }
    }
}
